// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/t_httpreply.cpp $
// $Rev: 295 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-28 09:25:49 +0000#$

#include "t_httpreply.h"
#include <typeinfo>

class T_HttpReply_3Private
{
public:
    T_HttpReply_3Private(T_HttpReply_3 *q)
        : q_ptr(q)
    {
        v_thread_count = 8;
        //v_thread_count = 1;
        //v_thread_count = 2;
        v_divided_size = 500000;
    }
    Q_DECLARE_PUBLIC(T_HttpReply_3)
    class T_HttpReply_3 *q_ptr;
    int v_thread_count;
    int v_divided_size;
    QQueue<T_Partial_Reply *> m_reply_queue;
    void gc()
    {
#if 0x1
        while(m_reply_queue.size()>0)
        {
            if(!m_reply_queue.head()->isFinished()) break;
            if(m_reply_queue.head()->bytesAvailable()>0) break;
            m_reply_queue.dequeue()->deleteLater();
        }
#endif
    }
    void wakeReplyQueue()
    {
        int v_size = qMin(v_thread_count, m_reply_queue.size());
        for(int i=0; i<m_reply_queue.size(); i++)
        {
            if(v_size==0) break;
            if(!m_reply_queue.value(i)->isFinished())
            {
                m_reply_queue.value(i)->wake();
                v_size--;
            }
        }
    }
    void adjustQueue()
    {
        this->gc();
        wakeReplyQueue();
    }
    bool isFinished()
    {
        for(int i=0; i<m_reply_queue.size(); i++)
        {
            bool v_finished = (m_reply_queue.value(i)->isFinished());
            //v_finished &= (m_reply_queue.head()->bytesAvailable()==0);
            if(!v_finished) return false;
        }
        return true;
    }
    qint64 bytesAvailable()
    {
        this->gc();
        qint64 v_total = 0;
        for(int i=0; i<m_reply_queue.size(); i++)
        {
            bool v_finished = m_reply_queue.value(i)->isFinished();
            v_total += m_reply_queue.value(i)->bytesAvailable();
            if(!v_finished) break;
        }
        //qDebug() << "[d->bytesAvailable()]" << v_total;
        return v_total;
    }
    QByteArray read(qint64 a_max)
    {
        this->gc();
        QByteArray v_total;
        qint64 v_length = a_max;
        for(int i=0; i<m_reply_queue.size(); i++)
        {
            if(v_length<=0) break;
            bool v_finished = m_reply_queue.value(i)->isFinished();
            QByteArray v_bytes = m_reply_queue.value(i)->just_read(v_length);
            v_total.append(v_bytes);
            v_length -= v_bytes.size();
            if(!v_finished) break;
        }
        Q_ASSERT(v_total.size()<=a_max);
        return v_total;
    }
};

T_HttpReply_3::T_HttpReply_3(HttpAccessBackend *parent)
    : NetworkHttpReply(parent)
    , m_headReply(0)
#if 0x0
T_HttpReply_3::T_HttpReply_3(
        QNetworkAccessManager::Operation op,
        QNetworkRequest request,
        QIODevice * outgoingData,
        QObject * parent)
            : NetworkHttpReply(op, request, outgoingData, parent)
            , m_headReply(0)
#endif
{
    this->d_ptr = new T_HttpReply_3Private(this);
    Q_D(T_HttpReply_3);

    Q_ASSERT(backend()->operation() == QNetworkAccessManager::GetOperation);

    //qDebug() << "[T_HttpReply_3::T_HttpReply_3()]" << m_url;
    // HEAD op
    m_headReply = QNetworkAccessManager::createRequest(
            backend()->operation(),
            backend()->request(),
            backend()->uploadDevice()
            );

    NetworkWaiter v_head_nbr(m_headReply);
    v_head_nbr.wait_bytesAvailable(1);
    //Q_ASSERT(m_headReply->bytesAvailable()>=1);
    QVariant v_content_type = m_headReply->header(QNetworkRequest::ContentTypeHeader);
    QVariant v_content_len = m_headReply->header(QNetworkRequest::ContentLengthHeader);
    qDebug() << "[T_HttpReply_3::T_HttpReply_3()]" << v_content_type << v_content_len;
    if(
            !v_content_type.toString().startsWith("video/")
            ||
            !v_content_len.isValid()
            )
    {
        enqueue_metaDataChanged();
        if(m_headReply->bytesAvailable())
        {
            enqueue_readyRead();
        }
        if(m_headReply->isFinished())
        {
            enqueue_finished();
            return;
        }
        p_connectQNetworkReply(m_headReply);
        return;
    }

    m_headReply->close();

    if(!v_content_len.isValid() || d->v_thread_count == 1)
    {
        d->m_reply_queue.enqueue(new T_Partial_Reply(backend()->request(), 0, -1));
    }
    else
    {
        qint64 v_offset = 0;
        qint64 v_total = v_content_len.toLongLong();
        while(v_total > 0)
        {
            qint64 v_length = qMin((qint64)d->v_divided_size, v_total);
            d->m_reply_queue.enqueue(new T_Partial_Reply(backend()->request(), v_offset, v_length));
            v_offset += v_length;
            v_total -= v_length;
        }
    }
    for(int i=0; i<d->m_reply_queue.size(); i++)
    {
        T_Partial_Reply *v_part_reply = d->m_reply_queue.value(i);
        connect(v_part_reply, SIGNAL(readyRead()), this, SLOT(head_readyRead()));
        connect(v_part_reply, SIGNAL(finished()), this, SLOT(head_finished()));
    }

    d->adjustQueue();

    //emit metaDataChanged(); //N.G.
    //QMetaObject::invokeMethod(this, "emit_metaDataChanged", Qt::QueuedConnection);
    enqueue_metaDataChanged();
}

// raw headers:
/* virtual */ bool T_HttpReply_3::hasRawHeader(const QByteArray &headerName) const
{
    return m_headReply->hasRawHeader(headerName);
}
/* virtual */ QList<QByteArray> T_HttpReply_3::rawHeaderList() const
{
    return m_headReply->rawHeaderList();
}
/* virtual */ QByteArray T_HttpReply_3::rawHeader(const QByteArray &headerName) const
{
    return m_headReply->rawHeader(headerName);
}

// attributes
/* virtual */ QVariant T_HttpReply_3::attribute(QNetworkRequest::Attribute code) const
{
    return m_headReply->attribute(code);
}

// reading
/* virtual */ qint64 T_HttpReply_3::bytesAvailable()
{
    Q_D(T_HttpReply_3);
    return d->bytesAvailable();
}
/* virtual */ QByteArray T_HttpReply_3::read(qint64 a_max)
{
    Q_D(T_HttpReply_3);
    return d->read(a_max);
}

void T_HttpReply_3::head_readyRead()
{
    Q_D(T_HttpReply_3);
    //qDebug() << "[T_HttpReply_3::head_readyRead()]" << d->bytesAvailable() << d->m_reply_queue.size();
    if(d->bytesAvailable()>0)
    {
        //emit readyRead();
        enqueue_readyRead();
    }
}

void T_HttpReply_3::head_finished()
{
    //qDebug() << "[T_HttpReply_3::head_finished()]";
    Q_D(T_HttpReply_3);
    d->adjustQueue();
    if(d->bytesAvailable()>0)
    {
        //emit readyRead();
        enqueue_readyRead();
    }
    if(d->isFinished())
    {
        //emit finished();
        enqueue_finished();
    }
}









T_HttpReply_1::T_HttpReply_1(HttpAccessBackend *parent)
    : NetworkHttpReply(parent)
{
    qDebug() << QString("ctor[%1::%1()]").arg(this->metaObject()->className()) << backend()->url();
    ////qDebug() << QString("ctor[%1::%1()]").arg(typeid(this).name()) << backend()->url();
    m_netReply = QNetworkAccessManager::createRequest(
            backend()->operation(),
            backend()->request(),
            backend()->uploadDevice()
            );
    // connect
    p_connectQNetworkReply(m_netReply);
}

// raw headers:
/* virtual */ bool T_HttpReply_1::hasRawHeader(const QByteArray &headerName) const
{
	return m_netReply->hasRawHeader(headerName);
}
/* virtual */ QList<QByteArray> T_HttpReply_1::rawHeaderList() const
{
	return m_netReply->rawHeaderList();
}
/* virtual */ QByteArray T_HttpReply_1::rawHeader(const QByteArray &headerName) const
{
	return m_netReply->rawHeader(headerName);
}

// attributes
/* virtual */ QVariant T_HttpReply_1::attribute(QNetworkRequest::Attribute code) const
{
	return m_netReply->attribute(code);
}

// reading
/* virtual */ qint64 T_HttpReply_1::bytesAvailable()
{
	return m_netReply->bytesAvailable();
}
/* virtual */ QByteArray T_HttpReply_1::read(qint64 a_max)
{
	return m_netReply->read(a_max);
}

// errorString()
/* virtual */ QString T_HttpReply_1::errorString() const
{
    if(m_netReply) return m_netReply->errorString();
    return "";
}

// SSL
/* virtual */ void T_HttpReply_1::ignoreSslErrors()
{
    if(m_netReply) m_netReply->ignoreSslErrors();
}
/* virtual */ void T_HttpReply_1::ignoreSslErrors(const QList<QSslError> & errors)
{
    if(m_netReply) m_netReply->ignoreSslErrors(errors);
}
/* virtual */ QSslConfiguration T_HttpReply_1::sslConfiguration() const
{
    if(m_netReply) return m_netReply->sslConfiguration();
    return QSslConfiguration::defaultConfiguration();
}
/* virtual */ void T_HttpReply_1::setSslConfiguration(const QSslConfiguration & config)
{
    if(m_netReply) m_netReply->setSslConfiguration(config);
}
